/* 
 * Copyright 2008 Rodrigo Reyes <reyes.rr at gmail dot com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
**/
package net.kornr.canvaschart.client.xy;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;

import com.google.gwt.widgetideas.graphics.client.Color;

import net.kornr.abstractcanvas.client.ICanvasExt;
import net.kornr.canvaschart.client.core.Annotation;
import net.kornr.canvaschart.client.core.AnnotationSupport;
import net.kornr.canvaschart.client.core.AnimationSupport;
import net.kornr.canvaschart.client.core.CoordinateSystem;
import net.kornr.canvaschart.client.core.Point;

public class XYBarRenderer implements XYRenderer, AnimationSupport, AnnotationSupport
{
	private double lineWidth;
	private String strokeColor = null;
	private String fillColor = null;
	private double m_progress = 1.0;
	// private int m_lastindex, m_lastoffset;
	private double m_barwidth;
	private Vector<BarCache> m_barstart = new Vector<BarCache>();
	private HashMap<XYDataSet, RenderingSpec> m_rends = new HashMap<XYDataSet, RenderingSpec>();
	private boolean enableAnnotation = true;
	
	public class BarCache
	{
		double xOffset;
		XYDataSet data;
		int dataOffset;
	}

	public class RenderingSpec
	{
		public Color strokecolor;
		public Color fillcolor;
	}

	public XYBarRenderer()
	{
		this(1.0d, "#000000", "#6666FF");
	}

	public XYBarRenderer(double linewidth, String htmlStrokeColor, String htmlFillColor)
	{
		this.lineWidth = linewidth;
		strokeColor = htmlStrokeColor;
		fillColor = htmlFillColor;
	}

	public void addDataSet(XYDataSet data, String htmlStrokeColor, String htmlFillColor)
	{
		RenderingSpec rs = new RenderingSpec();
		rs.strokecolor = new Color(htmlStrokeColor);
		rs.fillcolor = new Color(htmlFillColor);
		m_rends.put(data, rs);
	}

	public void render(ICanvasExt canvas, CoordinateSystem ground)
	{
		Set<XYDataSet> set = m_rends.keySet();
		int index = 0;

		int cachesize = 0;
		for (Iterator<XYDataSet> i = set.iterator(); i.hasNext(); )
		{
			XYDataSet ds = i.next();
			cachesize += ds.size()-1;
		}
		m_barstart.setSize(cachesize);

		int cacheoffset = 0;
		for (Iterator<XYDataSet> i = set.iterator(); i.hasNext(); )
		{
			XYDataSet ds = i.next();
			render(ds, ground, canvas, index++, set.size(), cacheoffset);
			cacheoffset += ds.size()-1;
		}
	}

	public void render(XYDataSet data, CoordinateSystem ground, ICanvasExt canvas, int barindex, int totalbarcount, int cacheoffset) 
	{
//		m_lastindex = barindex;
// 		m_lastoffset = totalbarcount;

		canvas.setLineWidth(1.0);

		RenderingSpec spec = m_rends.get(data);

		Point bottom = ground.getBottomRight();
		canvas.setStrokeStyle(spec.strokecolor);
		canvas.setFillStyle(spec.fillcolor);

		for (int i=0; i<data.size()-1; i++)
		{
			double x = data.getX(i);
			double y = data.getY(i);

			double nextx = data.getX(i+1);
			Point nextcoord = ground.getCoord(nextx, y);
			Point p = ground.getCoord(x,y);
			double width = nextcoord.getX() - p.getX(); // The total available width for all the bars at the same horizontal offset
			m_barwidth = (width/totalbarcount) - 4; // The width for a single bar = total width / number of bars, minus 4 pixels of separation
			double baroffset = (width/totalbarcount) * barindex + 2;

			double upper = bottom.getY() - ((bottom.getY()-p.getY())*m_progress);
			double barx = baroffset+p.getX();

			BarCache bc = m_barstart.get(cacheoffset+i);
			if (bc == null)
			{
				bc  = new BarCache();
				m_barstart.setElementAt(bc, cacheoffset+i);
			}
			
			bc.xOffset = barx;
			bc.data = data;
			bc.dataOffset = i;

			canvas.beginPath();
			canvas.moveTo(barx, upper);
			canvas.lineTo(barx+m_barwidth, upper);
			canvas.lineTo(barx+m_barwidth, bottom.getY());
			canvas.lineTo(barx, bottom.getY());
			canvas.lineTo(barx, upper);
			canvas.closePath();
			canvas.fill();
			canvas.stroke();
		}
	}

	public double getLineWidth() {
		return lineWidth;
	}

	public void setLineWidth(double lineWidth) {
		this.lineWidth = lineWidth;
	}

	public String getStrokeColor() {
		return strokeColor;
	}

	public void setStrokeColor(String strokeColor) {
		this.strokeColor = strokeColor;
	}

	public String getFillColor() {
		return fillColor;
	}

	public void setFillColor(String fillColor) {
		this.fillColor = fillColor;
	}

	public void setStep(double progress) {
		m_progress = progress;		
	}
	
	public void setEnableAnnotation(boolean b)
	{
		enableAnnotation = b;
	}

	public boolean updateAnnotationFor(CoordinateSystem coordsys, Point datacoord, Point pixelcoord, Annotation annotation) 
	{
		if (enableAnnotation == false)
			return false;
		
		for (int i=0; i<m_barstart.size()-1; i++)
		{
			BarCache bc = m_barstart.get(i);
			if (bc != null)
			{
				double x = bc.xOffset;
				if (x<=pixelcoord.getX() && pixelcoord.getX()<=x+m_barwidth)
				{
					Point dp = bc.data.get(bc.dataOffset);
					Point pco = coordsys.getCoord(dp);
					pco.setX(x + m_barwidth/2); // adjust the x to the real x
					
					if (annotation.getPoint().equals(pco))
						return false;
					
					annotation.setPoint(pco);
					annotation.setValue(dp.getY());
					annotation.setAlignement(Annotation.CENTER_UP);
					// GWT.log("point to " + pco + " from " + dp +" -- "+ m_barwidth + " @" + i + "=" + x, null);
//					return new Annotation(""+dp.getY(), pco);
					annotation.update();
					return true;
				}
			}
			else
			{
				// IGNORE
				// return null;
			}
		}

		return false;
	}

}
